﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace Jenvin.Library.Utils.Encryption
{
    public abstract class AbstractSysmEncryption : ISymEncryption
    {
        private byte[] _Key = null;
        private byte[] _IV = null;
        private string strKey = string.Empty;
        private SymmetricAlgorithm algorithm = null;

        public AbstractSysmEncryption()
        {
            algorithm = CreateCryptoServiceProvider();
        }
        /// <summary>
        /// 加密Key
        /// </summary>
        public virtual string Key
        {
            get
            {
                return System.Text.Encoding.ASCII.GetString(_Key);
            }
            set
            {
                string strTemp = value;
                if (strTemp.Length * 8 > algorithm.LegalKeySizes[0].MaxSize)
                {
                    // 如果密钥长度太长得话，截
                    strTemp = strTemp.Substring(0, algorithm.LegalKeySizes[0].MaxSize / 8);
                }
                if (strTemp.Length * 8 < algorithm.LegalKeySizes[0].MinSize)
                {
                    // 如果密钥长度太短得的话，在右边补空格
                    strTemp = strTemp.PadRight(algorithm.LegalKeySizes[0].MinSize / 8);
                }
                if (algorithm.LegalKeySizes[0].SkipSize != 0 && (strTemp.Length * 8) % algorithm.LegalKeySizes[0].SkipSize != 0)
                {
                    // 如果密钥长度不是密钥长度间隔单位的倍数，则补空格。
                    strTemp = strTemp.PadRight(algorithm.LegalKeySizes[0].MaxSize / 8);
                }

                _Key = System.Text.Encoding.ASCII.GetBytes(strTemp);
                algorithm.Key = _Key;
            }
        }
        /// <summary>
        /// 初始向量
        /// </summary>
        public virtual string IV
        {
            get
            {
                if (_IV == null) return null;
                return System.Text.Encoding.ASCII.GetString(_IV);
            }
            set
            {
                string strTemp = value;
                if (value.Length * 8 > algorithm.BlockSize)
                {
                    // 如果向量长度大于加密操作块的话，截
                    strTemp = value.Substring(0, algorithm.BlockSize / 8);
                }
                if (value.Length * 8 < algorithm.BlockSize)
                {
                    // 如果向量长度小于加密操作块的话，在右边补空格
                    strTemp = value.PadRight(algorithm.BlockSize / 8);
                }
                _IV = System.Text.Encoding.ASCII.GetBytes(strTemp);
                algorithm.IV = _IV;
            }
        }

        private byte[] GetIV()
        {
            if (_IV != null) return _IV;
            else
            {
                SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
                byte[] hash = sha1.ComputeHash(_Key);
                int ivSize = algorithm.LegalKeySizes[0].MinSize / 8;
                byte[] iv = new byte[ivSize];
                for (int x = 0; x < ivSize; ++x)
                {
                    iv[x] = hash[x];
                    //Key2Array[x] = Hash[x + 8];
                }
                return iv;
            }
        }
        /// <summary>
        /// Des加密 
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public virtual string Encrypt(string input)
        {
            if (string.IsNullOrEmpty(Key))
            {
                throw new InvalidOperationException("The key don't set.");
            }
            if (string.IsNullOrEmpty(input))
            {
                throw new ArgumentNullException("The input string can not be empty.");
            }
            byte[] Text = null;
            using (MemoryStream Stream = new MemoryStream())
            {
                using (MemoryStream inputStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(input)))
                {
                    Encrypt(inputStream, Stream);
                }
                Text = new byte[Stream.Length];
                Stream.Position = 0;
                Stream.Read(Text, 0, Text.Length);
            }
            return Convert.ToBase64String(Text, 0, (int)Text.Length);
        }
        /// <summary>
        /// 流加密。
        /// </summary>
        /// <param name="inputStream"></param>
        /// <param name="outputStream"></param>
        public virtual void Encrypt(Stream inputStream, Stream outputStream)
        {
            if (string.IsNullOrEmpty(Key))
            {
                throw new InvalidOperationException("The key don't set.");
            }
            if (inputStream == null || outputStream == null)
            {
                throw new ArgumentNullException("The input Stream or output Stream can not be null.");
            }

            algorithm.Key = _Key;
            algorithm.IV = GetIV();
            CryptoStream DESStream = new CryptoStream(outputStream, algorithm.CreateEncryptor(), CryptoStreamMode.Write);
            byte[] buffer = new byte[65536];
            int read = 0;
            while ((read = inputStream.Read(buffer, 0, 65536)) > 0)
            {
                DESStream.Write(buffer, 0, read);
                DESStream.Flush();
            }
            DESStream.FlushFinalBlock();
            algorithm.Clear();
        }

        /// <summary>
        /// 流解密
        /// </summary>
        /// <param name="inputStream"></param>
        /// <param name="outputStream"></param>
        public virtual void Decrypt(Stream inputStream, Stream outputStream)
        {
            if (string.IsNullOrEmpty(Key))
            {
                throw new InvalidOperationException("The key don't set.");
            }
            if (inputStream == null || outputStream == null)
            {
                throw new ArgumentNullException("The input Stream or output Stream can not be null.");
            }
            algorithm.Key = _Key;
            algorithm.IV = GetIV();

            CryptoStream DESStream = new CryptoStream(inputStream, algorithm.CreateDecryptor(), CryptoStreamMode.Read);
            byte[] buffer = new byte[65536];
            int read = 0;
            while ((read = DESStream.Read(buffer, 0, 65536)) > 0)
            {
                outputStream.Write(buffer, 0, read);
                outputStream.Flush();
            }
            algorithm.Clear();
        }
        /// <summary>
        /// Des 解密
        /// </summary>
        /// <param name="Input"></param>
        /// <param name="Key"></param>
        /// <returns></returns>
        public virtual string Decrypt(string input)
        {
            if (string.IsNullOrEmpty(Key))
            {
                throw new InvalidOperationException("The key don't set.");
            }
            if (string.IsNullOrEmpty(input))
            {
                throw new ArgumentNullException("The input string can not be empty.");
            }
            using (MemoryStream Stream = new MemoryStream(Convert.FromBase64String(input)))
            {
                using (MemoryStream ostream = new MemoryStream())
                {
                    Decrypt(Stream, ostream);
                    ostream.Position = 0;
                    using (StreamReader reader = new StreamReader(ostream, Encoding.UTF8))
                    {
                        return reader.ReadToEnd();
                    }
                }
            }
        }


        public abstract SymmetricAlgorithm CreateCryptoServiceProvider();

    }
}
